package com.mobialia.wikiplaces;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;

import com.google.android.maps.GeoPoint;

public class WikiplacesData implements Runnable {

	private static String TAG = "WikiPlacesData";
	private static WikiplacesData instance;

	GeoPoint locationGeoPoint;
	int distance;
	String lang = "en";
	Thread thread;
	int selectedPointIndex = -1;
	ArrayList<Wikiplace> wikiplaces;
	WikiplacesDataListener listener;
	SharedPreferences sharedPrefs;

	public static WikiplacesData getInstance(Context c) {
		if (WikiplacesData.instance == null) {
			WikiplacesData.instance = new WikiplacesData();
		}
		return WikiplacesData.instance;
	}

	private WikiplacesData() {
		wikiplaces = new ArrayList<Wikiplace>();
	}
	
	public synchronized void setLocationPoint(GeoPoint locationPoint) {
		this.locationGeoPoint = locationPoint;
	}

	public synchronized GeoPoint getLocationPoint() {
		return locationGeoPoint;
	}

	public void addListener(WikiplacesDataListener listener) {
		this.listener = listener;
	}

	public void removeListener(WikiplacesDataListener listener) {
		if (this.listener == listener) this.listener = null;
	}

	public GeoPoint getPointToDraw(int index) {
		return wikiplaces.get(index).getGeoPoint();
	}

	public void setSelectedPointIndex(int selectedPointIndex) {
		this.selectedPointIndex = selectedPointIndex;
	}

	public int getSelectedPointIndex() {
		return selectedPointIndex;
	}

	public GeoPoint getSelectedPoint() {
		if (selectedPointIndex == -1) return null;
		return wikiplaces.get(selectedPointIndex).getGeoPoint();
	}

	public int getSize() {
		return wikiplaces.size();
	}
	
	public int getNearestIndex(GeoPoint point) {
		double minDist = 999999999;
		int selectedIndex = -1;
		for (int i = 0; i < wikiplaces.size(); i++) {
			GeoPoint geoPoint = wikiplaces.get(i).getGeoPoint();
			double dist = DistanceCalculator.distance(point, geoPoint);

			if (dist < minDist) {
				selectedIndex = i;
				minDist = dist;
			}
		}
		return selectedIndex;
	}

	
	public void getDataFromServer(Context c) {
		if (locationGeoPoint == null) {
			Log.d(TAG, "Not updating data because location is not set");
			return;
		}
		sharedPrefs = PreferenceManager.getDefaultSharedPreferences(c);
		distance = Integer.valueOf(sharedPrefs.getString("distance", "20"));
		
		thread = new Thread(this);
		thread.start();
	}

	/**
	 * Gets data from server in JSON format
	 * 
	 * @param locationPoint
	 */
	public void run() {
		boolean error = false;
		if (listener != null)
			listener.startQuery();

		try {
			String url = "http://api.geonames.org/findNearbyWikipediaJSON?"+
				"lat="+(locationGeoPoint.getLatitudeE6()/1000000.0)
				+"&lng="+(locationGeoPoint.getLongitudeE6()/1000000.0)
				+"&radius=" + distance
				+"&maxRows=100"
				+"&lang=" + lang
				+"&username=mobialia";
			
			Log.d(TAG, url);
			InputStream is = openHttpConnection(url);

			String dataString = convertStreamToString(is);
			processResponseString(dataString);

		} catch (Exception e) {
			Log.e("JSON", "There was an error parsing the JSON", e);
			error = true;
		}
		if (listener != null) {
			if (error) {
				listener.endQueryError();
			} else {
				listener.endQueryOk();
			}
		}
	}

	private boolean processResponseString(String dataString) {
		try {
			if (dataString == null || dataString.length() == 0) return false;

			JSONObject base = new JSONObject(dataString);
			JSONArray data = base.getJSONArray("geonames");
			
			wikiplaces.clear();
			selectedPointIndex = -1;

			for (int i = 0; i < data.length(); i++) {
				JSONObject obj = data.getJSONObject(i);
				Log.d(TAG, obj.toString());
				
				GeoPoint wikiplaceGeoPoint = new GeoPoint((int) (obj.getDouble("lat")*1000000.0), (int) (obj.getDouble("lng")*1000000.0));

				Wikiplace wikiplace = new Wikiplace();
				wikiplace.setLat(obj.getDouble("lat"));
				wikiplace.setLng(obj.getDouble("lng"));
				wikiplace.setGeoPoint(wikiplaceGeoPoint);
				wikiplace.setTitle(obj.getString("title"));
				wikiplace.setSummary(obj.getString("summary"));
				wikiplace.setDistance(obj.getDouble("distance"));
				wikiplace.setRank(obj.getString("rank"));
				wikiplace.setWikipediaUrl(obj.getString("wikipediaUrl"));
				wikiplace.setElevation(obj.getDouble("elevation"));
				try {
					wikiplace.setFeature(obj.getString("feature"));
				} catch (JSONException e) {};
				try {
					wikiplace.setLang(obj.getString("lang"));
				} catch (JSONException e) {};
				try {
					wikiplace.setCountryCode(obj.getString("countryCode"));
				} catch (JSONException e) {};
				try {
					wikiplace.setThumbnailUrl(obj.getString("thumbnailUrl"));
				} catch (JSONException e) {};
				
				wikiplaces.add(wikiplace);
			}
		} catch (Exception e1) {
			e1.printStackTrace();
			return false;
		}
		return true;
	}

	public String convertStreamToString(InputStream is) throws IOException {
		Log.e(TAG, "convertStreamToString()");
		if (is != null) {
			StringBuilder sb = new StringBuilder();
			String line;

			try {
				BufferedReader reader = new BufferedReader(
						new InputStreamReader(is, "UTF-8"));
				while ((line = reader.readLine()) != null) {
					sb.append(line).append("\n");
				}
			} finally {
				is.close();
			}
			return sb.toString();
		} else {
			Log.e(TAG, "Error obtaining data!!!");
			return "";
		}
	}

	private InputStream openHttpConnection(String urlString) throws IOException {
		InputStream in = null;
		int response = -1;

		URL url = new URL(urlString);
		URLConnection conn = url.openConnection();

		if (!(conn instanceof HttpURLConnection))
			throw new IOException("Not an HTTP connection");

		try {
			HttpURLConnection httpConn = (HttpURLConnection) conn;
			httpConn.setAllowUserInteraction(false);
			httpConn.setInstanceFollowRedirects(true);
			httpConn.setRequestMethod("GET");
			httpConn.connect();

			response = httpConn.getResponseCode();
			if (response == HttpURLConnection.HTTP_OK) {
				in = httpConn.getInputStream();
			}
		} catch (Exception ex) {
			throw new IOException("Error connecting");
		}
		return in;
	}

	
	public void setLang(String lang) {
		this.lang = lang;
	}

	public Wikiplace getWikiplace(int i) {
		if (i < 0 || i >= wikiplaces.size()) return null;
		return wikiplaces.get(i);
	}
}
